﻿using S7.Net;
using S7.Net.Types;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TYBase;
using TYPLC.Omron;
using TYPLC.S7;
using TYRTUBase;
using DateTime = System.DateTime;

namespace TYPLC.PLC
{

    public class PlcCommConnector<T> : IPLCConnector, IDisposable
    {
        IPLCBase<T> plc;
        public PlcCommConnector(string section, IPLCBase<T> _plc)
        {
            this.Section = section;
            this.plc = _plc;
        }



        public bool IsSimulate { get; set; }

        public Dictionary<string, object> FakeData = new Dictionary<string, object>();

        private object objFake = new object();

        private void SaveData(string address, object val)
        {
            lock (objFake)
            {
                FakeData[address] = val;
            }
        }

        public object GetFakeData(string address)
        {
            lock (objFake)
            {
                if (FakeData.ContainsKey(address))
                {
                    return FakeData[address];
                }
            }
            return null;
        }

        private List<GroupParaEx> listGroup = new List<GroupParaEx>();


        public bool IsRun { get; private set; } = true;
        public string Section { get; internal set; }

        private bool isInitOK = true;

        public override bool Connect()
        {
            try
            {
                Log.I("Connect PLC START!");
                if (IsSimulate)
                {
                    isInitOK = true;
                    Log.I("IsSimulate true");
                    return true;
                }
                isInitOK = false;
                isInitOK = plc.Connect();
                Log.I($"Connect PLC Result :{isInitOK}!");
                return isInitOK;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"Connect PLC!");
                return false;
            }
        }

        private object lockObj = new object();

        public override bool AddGroup(GroupPara groupPara)
        {
            if (isInitOK == false)
            {
                Log.I($"  初始化异常!");
                return false;
            }
            lock (lockObj)
            {
                if (listGroup.Count(x => x.GroupName == groupPara.GroupName) >= 1)
                {
                    Log.I($"PLC init: has GroupName {groupPara.GroupName}");
                    return false;
                }
                List<TagConfig> listItem = new List<TagConfig>();
                listGroup.Add(new GroupParaEx
                {
                    GroupName = groupPara.GroupName,
                    ListTag = listItem,
                    IsActive = groupPara.IsActive
                });

                foreach (var item in groupPara.ListTag)
                {
                    if (!plc.CheckAddress(item.Address))
                    {
                        Log.I($"PLC init: address error :{item.Address}");
                        return false;
                    }
                    SaveData(item.Address, item.PreVal);
                    listItem.Add(item);
                }

                return true;
            }
        }

        bool GetAddress(string groupName, int index, out string address)
        {
            address = null;
            var groupData = listGroup.FirstOrDefault(x => x.GroupName == groupName);
            if (groupData == null)
                return false;
            var tag = groupData.ListTag.FirstOrDefault(x => x.DataIndex == index);
            if (tag == null)
                return false;
            address = tag.Address;
            return true;
        }

        public override bool Write(string groupName, int index, object value)
        {
            if (isInitOK == false)
                return false;
            try
            {
                Log.I($"PLC:Write-->{groupName}-->{index}-->{value}");
                if (!GetAddress(groupName, index, out var address))
                {
                    return false;
                }
                if (IsSimulate)
                {
                    SaveData(address, value);
                    return true;
                }
                return plc.Write(address, value);

            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Write error-->{groupName}-->{index}-->{value}");
                return false;
            }
        }

        public bool Write(string groupName, string address, object value)
        {
            if (isInitOK == false)
                return false;
            try
            {
                if (IsSimulate)
                {
                    SaveData(address, value);
                    return true;
                }
                return plc.Write(address, value);
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Write error-->{groupName}-->{address}-->{value}");
                return false;
            }
        }

        public bool Read(string groupName, string address, out object value)
        {
            if (isInitOK == false)
            {
                value = null;
                return false;
            }
            try
            {
                if (IsSimulate)
                {
                    value = GetFakeData(address);
                    return true;
                }
                return plc.Read(address, out value);
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Read error-->{groupName}-->{address}");
                value = null;
                return false;
            }
        }

        public override bool GetTagType(string groupName, int index, out PLCDataType dataType)
        {
            dataType = PLCDataType.BYTE;
            if (isInitOK == false)
            {
                return false;
            }
            try
            {
                var groupItem = listGroup.FirstOrDefault(x => x.GroupName == groupName);
                if (groupItem == null)
                    return false;
                var tag = groupItem.ListTag.FirstOrDefault(x => x.DataIndex == index);
                if (tag == null)
                    return false;
                dataType = tag.DataType;
                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC: GetTagType error-->{groupName}-->{index}");
                return false;
            }
        }
        public override bool Read(string groupName, int index, out object value)
        {
            if (isInitOK == false)
            {
                value = null;
                return false;
            }
            try
            {
                value = null;
                if (!GetAddress(groupName, index, out var address))
                {
                    return false;
                }

                if (IsSimulate)
                {
                    value = GetFakeData(address);
                    return true;
                }

                return plc.Read(address, out value);
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Read error-->{groupName}-->{index}");
                value = null;
                return false;
            }
        }

        private Thread thread;
        public override bool Start()
        {
            if (isInitOK == false)
                return false;
            thread = new Thread(() =>
            {
                while (IsRun)
                {
                    DateTime dt = DateTime.Now;
                    foreach (var groupItem in listGroup)
                    {
                        if (groupItem.IsActive)
                        {
                            if (dt - groupItem.LastCheckTime < TimeSpan.FromMilliseconds(groupItem.CheckTimes))
                            {
                                continue;
                            }
                            foreach (var tag_item in groupItem.ListTag)
                            {
                                if (IsRun == false) return;
                                {
                                    if (IsSimulate)//模拟信号
                                    {
                                        var data = GetFakeData(tag_item.Address);
                                        if (data is bool bool_val && bool_val == true)
                                        {
                                            FireDatChange(groupItem.GroupName, tag_item.DataIndex, bool_val);
                                        }
                                    }
                                }
                                {
                                    if (!plc.Read(tag_item.Address, out var data))//读取plc 数据,这里只管bool类型
                                    {
                                        continue;
                                    }
                                    if (IsRun == false) return;

                                    if (data is bool _b)
                                    {
                                        if (tag_item.PreVal is bool _preb)
                                        {
                                            if (_b != _preb)
                                            {
                                                FireDatChange(groupItem.GroupName, tag_item.DataIndex, _b);
                                            }
                                        }
                                        else
                                        {
                                            FireDatChange(groupItem.GroupName, tag_item.DataIndex, _b);
                                        }
                                        tag_item.PreVal = _b;
                                    }
                                    else if (data is byte _byte)
                                    {

                                        if (tag_item.PreVal is byte _preb)
                                        {
                                            if (_byte != _preb)
                                            {
                                                FireDatChange(groupItem.GroupName, tag_item.DataIndex, _byte);
                                            }
                                        }
                                        else
                                        {
                                            FireDatChange(groupItem.GroupName, tag_item.DataIndex, _byte);
                                        }
                                        tag_item.PreVal = _byte;
                                    }
                                    else if (data is int _int)
                                    {

                                        if (tag_item.PreVal is int _preb)
                                        {
                                            if (_int != _preb)
                                            {
                                                FireDatChange(groupItem.GroupName, tag_item.DataIndex, _int);
                                            }
                                        }
                                        else
                                        {
                                            FireDatChange(groupItem.GroupName, tag_item.DataIndex, _int);
                                        }
                                        tag_item.PreVal = _int;
                                    }
                                    else if (data is short _short) {

                                        if (tag_item.PreVal is short _preb) {
                                            if (_short != _preb) {
                                                FireDatChange(groupItem.GroupName, tag_item.DataIndex, _short);
                                            }
                                        }
                                        else {
                                            FireDatChange(groupItem.GroupName, tag_item.DataIndex, _short);
                                        }
                                        tag_item.PreVal = _short;
                                    }

                                    else if (data is ushort _ushort) {

                                        if (tag_item.PreVal is ushort _preb) {
                                            if (_ushort != _preb) {
                                                FireDatChange(groupItem.GroupName, tag_item.DataIndex, _ushort);
                                            }
                                        }
                                        else {
                                            FireDatChange(groupItem.GroupName, tag_item.DataIndex, _ushort);
                                        }
                                        tag_item.PreVal = _ushort;
                                    }

                                }
                            }
                        }
                    }
                }
            })
            { IsBackground = true };
            thread.Start();
            return true;
        }

        public void Dispose()
        {
            IsRun = false;
        }
    }
    public class PLCConnector_S71500 : PlcCommConnector<DataItem>
    {
        public PLCConnector_S71500(string ip, string section) : base(section, new S7PLC_1500(ip))
        {
        }
    }

    public class PLCConnector_OmornRTU : PlcCommConnector<OmronAddressData>
    {
        public PLCConnector_OmornRTU(int com, string section, ComPara para) : base(section, new OmronPLC(new TYRTUPComm(com, para)))
        {

        }
    }

  
}
